// Copyright 2018 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// feature: support multiple sending based on topic
// ------------------------------------------------------------------

#ifndef SRC_CPP_RTPS_TRANSPORT_TCPV4TRANSPORT_H_
#define SRC_CPP_RTPS_TRANSPORT_TCPV4TRANSPORT_H_

#include <thread>
#include <vector>
#include <map>
#include <mutex>

#include <asio.hpp>
#include <transport/TCPv4TransportDescriptor.h>
#include <deps/common/IPFinder.h>
#include <TCPTransportInterface.h>
#include <tcp/RTCPHeader.h>

namespace vbs {
namespace transport {

/**
 * This is a default TCPv4 implementation.
 *    - Opening an output channel by passing a remote locator will try to open a TCP conection with
 * the endpoint. If there is created a connection with the same endpoint, the transport will use the
 * same one.
 *
 *    - It is possible to provide a white list at construction, which limits the interfaces the
 * transport will ever be able to interact with. If left empty, all interfaces are allowed.
 *
 *    - Opening an input channel by passing a locator will open a socket listening on the given
 * physical port on every whitelisted interface, it will wait for incoming connections until the
 * receiver closes the channel. Several endpoints can connect to other to the same physical port,
 * because the OS creates a connection socket after each establishment.
 * @ingroup TRANSPORT_MODULE
 */
class TCPv4Transport : public TCPTransportInterface {
 protected:
    TCPv4TransportDescriptor configuration_;
    std::vector<WhiteListAddr> interface_whitelist_;

    //! Constructor with no descriptor is necessary for implementations derived from this class.
    TCPv4Transport();

    bool compare_locator_ip(const Locator& lh, const Locator& rh) const override;

    bool compare_locator_ip_and_port(const Locator& lh, const Locator& rh) const override;

    void fill_local_ip(Locator& loc) const override;

    asio::ip::tcp::endpoint generate_endpoint(uint16_t port) const override;

    asio::ip::tcp::endpoint generate_endpoint(const Locator& loc, uint16_t port) const override;

    asio::ip::tcp::endpoint generate_local_endpoint(Locator& loc, uint16_t port) const override;

    asio::ip::tcp generate_protocol() const override;

    asio::ip::tcp get_protocol_type() const override { return asio::ip::tcp::v4(); }

    void get_ips(std::vector<vbsutil::xmlparser::IPFinder::info_IP>& locNames,
                 bool return_loopback = false) const override;

    /**
     * Method to get a list of interfaces to bind the socket associated to the given locator.
     * @return Vector of interfaces in string format.
     */
    //  std::vector<std::string> get_binding_interfaces_list() override;

    bool is_locator_allowed(const Locator& locator) const override;

    //  //! Checks if the interfaces white list is empty.
    //  bool is_interface_whitelist_empty() const override;

    void set_receive_buffer_size(uint32_t size) override;
    void set_send_buffer_size(uint32_t size) override;

    void endpoint_to_locator(const asio::ip::tcp::endpoint& endpoint, Locator& locator) const override;

 public:
    RTPS_DllAPI TCPv4Transport(const TCPv4TransportDescriptor&);

    ~TCPv4Transport() override;

    const TCPTransportDescriptor* configuration() const override;

    TCPTransportDescriptor* configuration() override;

    LocatorList NormalizeLocator(const Locator& locator) override;

    bool is_local_locator(const Locator& locator) const override;

    TransportDescriptorInterface* get_configuration() override { return &configuration_; }

    void AddDefaultOutputLocator(LocatorList&) override;

    bool fillMetatrafficUnicastLocator(Locator& locator, uint32_t metatraffic_unicast_port) const override;

    bool fillUnicastLocator(Locator& locator, uint32_t well_known_port) const override;
};

}  // namespace transport
}  // namespace vbs
#endif  // SRC_CPP_RTPS_TRANSPORT_TCPV4TRANSPORT_H_
